package com.atguigu.tingshu.user.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.MongoUtil;
import com.atguigu.tingshu.model.user.UserListenProcess;
import com.atguigu.tingshu.user.service.UserListenProcessService;
import com.atguigu.tingshu.vo.album.TrackStatMqVo;
import com.atguigu.tingshu.vo.user.UserListenProcessVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserListenProcessServiceImpl implements UserListenProcessService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    /**
     * 查询用户对于某个声音的播放进度
     *
     * @param userId  用户ID
     * @param trackId 声音ID
     * @return
     */
    @Override
    public BigDecimal getTrackBreakSecond(Long userId, Long trackId) {
        //1. 获取当前用户播放进度集合名称 播放进度集合前缀_用户ID
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);

        //2. 根据用户ID+声音ID查询播放进度
        //2.1 创建查询对象 封装2个查询条件
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId).where("trackId").is(trackId));
        UserListenProcess userListenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
        if (userListenProcess != null) {
            return userListenProcess.getBreakSecond();
        }
        return new BigDecimal("0");
    }

    /**
     * 只要在播放器内播放声音，就是定时每隔10s进行一次调用
     * 更新当前用户对于某个声音播放进度
     *
     * @param userListenProcessVo
     * @return
     */
    @Override
    public void updateListenProcess(Long userId, UserListenProcessVo userListenProcessVo) {
        //1. 获取当前用户播放进度集合名称 播放进度集合前缀_用户ID
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);

        //2.根据用户ID+声音ID查询播放进度
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId).where("trackId").is(userListenProcessVo.getTrackId()));
        UserListenProcess userListenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
        //3. 如果查询到播放进度，则更新播放进度
        if (userListenProcess != null) {
            userListenProcess.setBreakSecond(userListenProcessVo.getBreakSecond().setScale(0, RoundingMode.HALF_UP));
            userListenProcess.setUpdateTime(new Date());
        } else {
            //4. 如果未查询到播放进度，则新增播放进度
            userListenProcess = new UserListenProcess();
            userListenProcess.setUserId(userId);
            userListenProcess.setAlbumId(userListenProcessVo.getAlbumId());
            userListenProcess.setTrackId(userListenProcessVo.getTrackId());
            userListenProcess.setBreakSecond(userListenProcessVo.getBreakSecond().setScale(0, RoundingMode.HALF_UP));
            userListenProcess.setUpdateTime(new Date());
            userListenProcess.setCreateTime(new Date());
        }
        //4.保存或更新播放进度
        mongoTemplate.save(userListenProcess, collectionName);

        //5. 增量更新声音/专辑统计数值 由于更新播放进度接口定时调用，所以不能每次调用都需更新播放量

        //5.1 生产消息幂等性（某个用户对某个声音播放进度增量当前内只能加1） 采用Redis set nx 命令来确保幂等性
        //5.1.1 构建幂等性唯一标识=前缀:用户ID_声音ID
        String key = RedisConstant.USER_TRACK_REPEAT_STAT_PREFIX + userId + "_" + userListenProcessVo.getTrackId();
        try {
            //5.1.2 计算解决当日内幂等性key的过期时间，当天结束时间减去当前时间
            DateTime dateTime = DateUtil.endOfDay(new Date());
            long ttl = dateTime.getTime() - System.currentTimeMillis();
            //5.1.3 尝试将key存入redis
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", ttl, TimeUnit.MILLISECONDS);
            //5.2 如果是当前日首次增量更新统计数值，发送RabbitMQ消息 通知专辑、搜索服务更新各自统计数值
            if (flag) {
                //5.2.1 构建增量更新声音/专辑统计MQ消息对象，包含：用户ID，专辑ID，声音ID，增量值，统计类型，业务唯一标识（用于消费者端幂等性使用）
                // 基于RabbitMQ发送自定义对象，必须要实现序列化接口
                TrackStatMqVo mqVo = new TrackStatMqVo();
                mqVo.setBusinessNo(IdUtil.randomUUID());
                mqVo.setAlbumId(userListenProcessVo.getAlbumId());
                mqVo.setTrackId(userListenProcessVo.getTrackId());
                mqVo.setStatType(SystemConstant.TRACK_STAT_PLAY);
                mqVo.setCount(1);
                //5.2.2 发送RabbitMQ消息
                rabbitService.sendMessage(MqConst.EXCHANGE_TRACK, MqConst.ROUTING_TRACK_STAT_UPDATE, mqVo);
            }
        } catch (Exception e) {
            log.error("[用户服务]统计值增量更新异常：{}", e);
            redisTemplate.delete(key);
        }
    }


    public static void main(String[] args) {
        DateTime dateTime = DateUtil.endOfDay(new Date());
        System.out.println(dateTime.getTime() - System.currentTimeMillis());
    }


}
